#include "../../include/sm2_params.h"
#include <gmssl/sm4.h>
#include <gmssl/rand.h>
#include <string.h>
#include <stdlib.h>

/**
 * @brief SM4 CBC模式加密
 * @param input 明文输入
 * @param length 明文长度
 * @param key 密钥（16字节）
 * @param iv 初始化向量（16字节）
 * @param output 密文输出缓冲区
 */
void sfts_sm4_cbc_encrypt(const unsigned char *input, size_t length,
                          const unsigned char *key,
                          unsigned char *iv,
                          unsigned char *output)
{
    SM4_KEY sm4_key;
    unsigned char ivec[16];
    memcpy(ivec, iv, 16);

    // 设置加密密钥
    sm4_set_encrypt_key(&sm4_key, key);

    // PKCS7填充处理
    size_t pad = 16 - (length % 16);
    size_t total_len = length + pad;
    unsigned char *padded_data = malloc(total_len);
    memcpy(padded_data, input, length);
    memset(padded_data + length, pad, pad);

    // CBC模式加密
    for (size_t i = 0; i < total_len; i += 16)
    {
        for (int j = 0; j < 16; j++)
            padded_data[i + j] ^= ivec[j];
        sm4_encrypt(&sm4_key, padded_data + i, output + i);
        memcpy(ivec, output + i, 16);
    }

    // 清除敏感数据
    volatile unsigned char *p = (volatile unsigned char *)&sm4_key;
    for (size_t i = 0; i < sizeof(SM4_KEY); i++)
        p[i] = 0;

    p = (volatile unsigned char *)padded_data;
    for (size_t i = 0; i < total_len; i++)
        p[i] = 0;

    free(padded_data);
}

/**
 * @brief 生成SM4-CBC模式初始化向量
 * @param iv 输出缓冲区（必须16字节）
 * @return 0成功 -1熵源不足
 * @details 使用GmSSL rand_bytes随机源
 */
int generate_iv(unsigned char *iv)
{
    // 使用GmSSL rand_bytes接口
    return rand_bytes(iv, 16) == 1 ? 0 : -1;
}

/**
 * @brief 验证SM4加密数据的PKCS#7填充
 * @param buf 待验证的密文缓冲区
 * @param len 数据长度（必须大于等于16字节）
 * @return 1-填充正确 0-填充错误
 * @details 检查最后一个字节的填充值，验证所有填充字节的一致性
 * @warning 需在解密完成后调用
 */
int verify_padding(const unsigned char *buf, int len)
{
    uint8_t pad = buf[len - 1];
    if (pad > 16 || pad < 1)
        return 0;
    for (int i = 1; i <= pad; i++)
    {
        if (buf[len - i] != pad)
            return 0;
    }
    return 1;
}